//
//  JKEmojiKeyboardPackage.swift
//  表情键盘
//
//  Created by albert on 16/6/26.
//  Copyright © 2016年 albert. All rights reserved.
//  加载表情数据

import UIKit

/**
 说明：
 1. Emoticons.bundle 的根目录下存放的 emoticons.plist 保存了 packages 表情包信息
     >packages 是一个数组, 数组中存放的是字典
     >字典中的属性 id 对应的分组路径的名称
 2. 在 id 对应的目录下，各自都保存有 info.plist
     >group_name_cn   保存的是分组名称
     >emoticons       保存的是表情信息数组
     >code            UNICODE 编码字符串
     >chs             表情文字，发送给新浪微博服务器的文本内容
     >png             表情图片，在 App 中进行图文混排使用的图片
 */

class JKEmojiKeyboardPackage: NSObject {
    /** 当前组对应的文件夹名称 */
    var id: String?
    
    /** 当前组的名称 */
    var group_name_cn: String?
    
    /** 当前组的所有表情 */
    var emoticons: [JKEmojiModel]?
    
    init(id: String) {
        self.id = id
    }
    
    /** 加载所有组数据 */
    class func loadEmojiPachages() -> [JKEmojiKeyboardPackage] {
        
        var models = [JKEmojiKeyboardPackage]()
        // 0.手动添加最近组
        let package = JKEmojiKeyboardPackage(id: "")
        package.appendEmptyEmojis()
        models.append(package)
        
        // 1.加载emoticons.plist文件
        // 1.1获取emoticons.plist文件路径
        let path = NSBundle.mainBundle().pathForResource("emoticons.plist", ofType: nil, inDirectory: "Emoticons.bundle")!
        // 1.2加载emoticons.plist
        let dict = NSDictionary(contentsOfFile: path)!
        let array = dict["packages"] as! [[String : AnyObject]]
        
        // 2.取出所有组表情
        for packageDict in array {
            // 2.1创建当前组模型
            let package = JKEmojiKeyboardPackage(id: packageDict["id"] as! String)
            // 2.2加载当前组所有的表情数据
            package.loadEmojiIcons()
            // 2.3补全一组数据，保证当前组能被21整除
            package.appendEmptyEmojis()
            // 2.4将当前组模型添加到数组中
            models.append(package)
        }
        return models
    }
    
    /** 加载所有表情 */
    private func loadEmojiIcons() {
        // 1.拼接当前组info.plist路径
        let path = NSBundle.mainBundle().pathForResource(self.id, ofType: nil, inDirectory: "Emoticons.bundle")!
        let filePath = (path as NSString).stringByAppendingPathComponent("info.plist")
        // 2.根据路径加载info.plist文件
        let dict = NSDictionary(contentsOfFile: filePath)!
        
        // 3.从加载进来的字典中取出当前组数据
        // 3.1取出当前组名称
        group_name_cn = dict["group_name_cn"] as? String
        
        // 3.2取出当前组所有表情
        let array = dict["emoticons"] as! [[String : AnyObject]]
        
        // 3.3遍历数组，取出每一个表情
        var models = [JKEmojiModel]()
        
        var index = 0
        for emojiDict in array {
            
            // 先添加模型
            let model = JKEmojiModel(dict: emojiDict, id: self.id!)
            models.append(model)
            index++
            
            // 到了第21个，就添加为删除按钮
            if index == 20 {
                let emoticon = JKEmojiModel(isDeleteButton: true)
                models.append(emoticon)
                index = 0
                continue
            }
        }
        emoticons = models
    }
    
    /** 补全一组数据, 保证当前组能被21整除 */
    private func appendEmptyEmojis() {
        
        // 0.判断是否是最近组
        if emoticons == nil {
            emoticons = [JKEmojiModel]()
        }
        
        // 1.取出不能被21整除剩余的个数
        let number = emoticons!.count % 21
        
        // 2.如果已经被整除了，且不是最近组，直接返回
        if number == 0 && emoticons?.count > 0 {
            return
        }
        
        // 3.如果不能被整除，补全为空白
        for _ in number..<20 {
            let emoticon = JKEmojiModel(isDeleteButton: false)
            emoticons?.append(emoticon)
        }
        
        // 4.补全删除按钮
        let emoticon = JKEmojiModel(isDeleteButton: true)
        emoticons?.append(emoticon)
    }
    
    /** 添加最近表情 */
    func addLatestEmoji(emoticon: JKEmojiModel) {
        
        emoticons?.removeLast()
        
        // 1.判断当前表情是否已经添加过了
        if !emoticons!.contains(emoticon) {
            // 2.添加当前点击的表情到最近组
            emoticons?.removeLast()
            emoticons?.append(emoticon)
        }
        
        // 3.对表情进行排序
        let array =  emoticons?.sort({ (e1, e2) -> Bool in
            return e1.useCount > e2.useCount
        })
        emoticons = array
        
        // 4.添加删除按钮
        emoticons?.append(JKEmojiModel(isDeleteButton: true))
    }
}

// MARK: - 微博正文匹配
extension JKEmojiKeyboardPackage {
    
    /** 微博正文匹配，创建正文的属性字符串，图文显示 */
    class func createMutableAttrString(str : String, font : UIFont) -> NSMutableAttributedString {
        // 创建匹配规则
        // 表情的规则
        //let emoticonPattern = "\\[.*?\\]" // 规则转义和字符串转义
        
        // @名字规则
        let namePattern = "@([0-9a-zA-Z\\u4e00-\\u9fa5_-]+)" //"@.*?:"
        
        // 匹配话题 #...#
        let topicPattern = "#.*?#"
        
        // 匹配URL
        let purlPattern = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?"
        
        // 根据要匹配的字符串创建属性字符串
        var attrMStr = NSMutableAttributedString(string: str)
        
        // 依次匹配url、话题、@名字、表情
        attrMStr = matchText(str, pattern: purlPattern, attrMStr: attrMStr, font: font)
        attrMStr = matchText(str, pattern: topicPattern, attrMStr: attrMStr, font: font)
        attrMStr = matchText(str, pattern: namePattern, attrMStr: attrMStr, font: font)
        
        return matchEmoticon(str, attrMStr: attrMStr, font: font)
    }
    
    /** 匹配文字（@名字规则、话题、URL） */
    private class func matchText(str: String, pattern: String, attrMStr: NSMutableAttributedString, font : UIFont) -> NSMutableAttributedString {
        
        // 1.利用对象匹配字符串
        let regex = try! NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.CaseInsensitive)
        
        // 2.获取匹配结果
        let results = regex.matchesInString(str, options: NSMatchingOptions(rawValue: 0), range: NSRange(location: 0, length: str.characters.count))
        
        // 3.遍历结果
        //for result in results {
        // 因为从前往后遍历替换会出先range不准的问题，因为第一个表情被替换了，导致第二个range替换错了
        // 所以从后往前遍历替换
        for var i = results.count - 1; i >= 0; i-- {
            // 3.1取出匹配结果的chs
            let result = results[i]
            
            // 3.2获取range
            let resultRange = result.range
            
            // 3.3获取要替换的文字
            let urlStr = (str as NSString).substringWithRange(resultRange)
            // 3.4替换文字，将之前字符串中对应文字替换有颜色的文字
            let urlAttrStr = NSAttributedString(string: urlStr, attributes: [NSForegroundColorAttributeName : UIColor(red: 94.0/255.0, green: 132.0/255.0, blue: 177.0/255.0, alpha: 1)])
            attrMStr.replaceCharactersInRange(resultRange, withAttributedString: urlAttrStr)
        }
        
        // 4.返回
        return attrMStr
    }
    
    /** 匹配表情 */
    private class func matchEmoticon(str: String, attrMStr: NSMutableAttributedString, font : UIFont) -> NSMutableAttributedString {
        
        // 0.匹配表情的规则
        let pattern = "\\[.*?\\]" // 规则转义和字符串转义
        
        // 1.利用对象匹配字符串
        let regex = try! NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.CaseInsensitive)
        
        // 2.获取匹配结果
        let results = regex.matchesInString(str, options: NSMatchingOptions(rawValue: 0), range: NSRange(location: 0, length: str.characters.count))
        
        // 3.根据要匹配的字符创建一个属性字符串（由外接传入参数）
        //let attrMStr = NSMutableAttributedString(string: str)
        
        // 4.遍历结果
        //for result in results {
        // 因为从前往后遍历替换会出先range不准的问题，因为第一个表情被替换了，导致第二个range替换错了
        // 所以从后往前遍历替换
        for var i = results.count - 1; i >= 0; i-- {
            // 4.1取出匹配结果的chs
            let result = results[i]
            let chs = (str as NSString).substringWithRange(result.range)
            
            // 4.2查找chs对应的pngPath
            let pngPath = findPath(chs)
            
            // 4.3根据路径创建表情图片的属性字符串
            guard let tempPngPath = pngPath else {
                continue
            }
            let attachment = NSTextAttachment()
            let fontHeight = font.lineHeight
            attachment.bounds = CGRect(x: 0, y: -4, width: fontHeight, height: fontHeight)
            attachment.image = UIImage(contentsOfFile: tempPngPath)
            let emoticonAttrStr = NSAttributedString(attachment: attachment)
            
            // 4.4将之前字符串中chs替换成表情
            attrMStr.replaceCharactersInRange(result.range, withAttributedString: emoticonAttrStr)
        }
        
        // 5.返回
        return attrMStr
    }
    
    /** 遍历获取表情名称对应的绝对路径pngPath */
    private class func findPath(chs: String) -> String? {
        // 1.加载所有组表情
        let packages = JKEmojiKeyboardPackage.loadEmojiPachages()
        
        // 2.遍历所有组表情，拿到chs对应的pngPath
        for package in packages {
            // 2.1如果该表情组没有值，则该表情组不需要再进行遍历
            guard let emoticons = package.emoticons else {
                print("该组没有表情")
                continue
            }
            
            // 2.2定义表情pngPath
            var pngPath: String?
            
            // 2.3遍历每一个表情组中的表情
            for emoticon in emoticons {
                // 2.3.1如果该表情的chs为空，则继续遍历下一个表情
                guard let emoticonChs = emoticon.chs else {
                    continue
                }
                
                // 2.3.2如果chs不为空,则判断表情的chs是否和传入的chs相等
                if emoticonChs == chs {
                    pngPath = emoticon.pngPath
                    break
                }
            }
            
            //2.4如果遍历了一个表情组中已经取出了pngPath,则不需要再遍历下一个表情包，直接返回pngPath
            if pngPath != nil {
                return pngPath
            }
        }
        
        // 3.如果遍历了所以的表情包依然没有值,则直接返回nil
        return nil
    }
}

// MARK: - 每一个表情的模型数据
class JKEmojiModel: NSObject {
    
    /** 当前组对应的文件夹名称 */
    var id: String?
    
    /** 表情的中文名称 */
    var chs: String?
    
    /** 表情对应的图片 */
    var png: String? {
        didSet {
            let path = NSBundle.mainBundle().pathForResource(id, ofType: nil, inDirectory: "Emoticons.bundle")!
            pngPath = (path as NSString).stringByAppendingPathComponent(png ?? "")
        }
    }
    
    /** 表情对应的图片的绝对路径 */
    var pngPath: String?
    
    /** emoji表情编码 */
    var code: String? {
        didSet {
            // 1.创建一个扫描器
            let scanner = NSScanner(string: code ?? "")
            
            // 2.从字符串中扫描出对应的16进制数
            var result: UInt32 = 0
            scanner.scanHexInt(&result)
            // 3.根据扫描出的16进制创建一个字符串
            emoticonStr = "\(Character(UnicodeScalar(result)))"
        }
    }
    
    /** 转换之后的emoji表情字符串 */
    var emoticonStr: String?
    
    /** 记录当前是否是删除按钮 */
    var isDeleteButton: Bool = false
    
    /** 记录当前表情的使用次数 */
    var useCount: Int = 0
    
    init(dict: [String: AnyObject], id: String) {
        super.init()
        
        self.id = id
        setValuesForKeysWithDictionary(dict)
    }
    
    init(isDeleteButton: Bool) {
        self.isDeleteButton = isDeleteButton
    }
    
    override func setValue(value: AnyObject?, forUndefinedKey key: String) {
        
    }
}